package com.cocotingo.snail.template;

import java.util.Map;

import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

class ParserUtility {

	static boolean isWhitespacedTextNode(Node node) {
		if (node.getNodeType() != Node.TEXT_NODE) return false;
		String text = node.getTextContent();
		for (int i = 0; i < text.length(); i++) {
			if (!Character.isWhitespace(text.charAt(i))) return false;
		}
		return true;
	}
	
	static void getRequiredAttributesFromNodeStrictly(Node node, Map<String, String> valueMap, String currentNodeName) throws TemplateParserException {
		NamedNodeMap attrs = node.getAttributes();
		for (int i = 0; i < attrs.getLength(); i++) {
			Node attr = attrs.item(i);
			String key = attr.getNodeName();
			if (valueMap.containsKey(key)) {
				valueMap.put(key, attr.getNodeValue());
			} else {
				throw new TemplateParserException("unknown attribute on " + currentNodeName + " node: " + attr);
			}
		}
		for (String key : valueMap.keySet()) {
			if (valueMap.get(key) == null) {
				throw new TemplateParserException("missing " + key + " attribute on " + currentNodeName + " node");
			}
		}
	}
	
	static void getOptionalAttributesFromNodeStrictly(Node node, Map<String, String> valueMap, String currentNodeName) throws TemplateParserException {
		NamedNodeMap attrs = node.getAttributes();
		for (int i = 0; i < attrs.getLength(); i++) {
			Node attr = attrs.item(i);
			String key = attr.getNodeName();
			if (valueMap.containsKey(key)) {
				valueMap.put(key, attr.getNodeValue());
			} else {
				throw new TemplateParserException("unknown attribute on " + currentNodeName + " node: " + attr);
			}
		}
	}
	
	static void ensureNoChildrenOnNode(Node node, String currentNodeName) throws TemplateParserException {
		NodeList nodes = node.getChildNodes();
		for (int i = 0; i < nodes.getLength(); i++) {
			Node n = nodes.item(i);
			if (n.getNodeType() != Node.COMMENT_NODE) {
				if (n.getNodeType() == Node.TEXT_NODE && isWhitespacedTextNode(n)) {
					continue;
				}
				throw new TemplateParserException("unknown child node on " + currentNodeName + " node: " + n);
			}
		}
	}
	
	static void ensureNoAttributesOnNode(Node node, String currentNodeName) throws TemplateParserException {
		NamedNodeMap attrs = node.getAttributes();
		if (attrs.getLength() != 0) throw new TemplateParserException("unknown attribute on " + currentNodeName + " node: " + attrs.item(0));
	}
	
	static int[] getAnchorValuesFromString(String str) throws TemplateParserException {
		if (str == null || str.isEmpty()) throw new TemplateParserException("missing anchors values, should be: x1,y1,x2,y2");
		String[] strs = str.split(",");
		if (strs.length < 4) throw new TemplateParserException("missing anchors values, should be: x1,y1,x2,y2");
		if (strs.length > 4) throw new TemplateParserException("too many anchors values, should be: x1,y1,x2,y2");
		int[] values = new int[4];
		int i = 0;
		try {
			for (; i < 4; i++) {
				values[i] = Integer.parseInt(strs[i].trim());
			}
		} catch (NumberFormatException e) {
			throw new TemplateParserException("incorrect anchor value format: " + strs[i].trim());
		}
		return values;
	}
	
	static int getColorValueFromString(String str) throws TemplateParserException {
		if (str == null || str.isEmpty()) throw new TemplateParserException("missing rgb values, should be: r,g,b or 0xrrggbb");
		if (str.startsWith("0x")) {
			// 0xrrggbb
			int rst = 0;
			int j = 0;
			for (int i = str.length() - 1; i >= 2; i--, j += 4) {
				rst += hexCharacterToInteger(str.charAt(i)) << j;
			}
			return rst;
		} else {
			// r, g, b
			String[] strs = str.split(",");
			if (strs.length < 3) throw new TemplateParserException("missing rgb values, should be: r,g,b");
			if (strs.length > 3) throw new TemplateParserException("too many rgb values, should be: r,g,b");
			int rst = 0;
			int i = 0;
			try {
				for (; i < 3; i++) {
					int el = Integer.parseInt(strs[i].trim());
					if (el < 0 || el > 255) throw new TemplateParserException("invalid rgb value: " + el);
					rst |= el << (8 * (2 - i));
				}
			} catch (NumberFormatException e) {
				throw new TemplateParserException("incorrect rgb value format: " + strs[i].trim());
			}
			return rst;
		}
	}
	
	private static int hexCharacterToInteger(char ch) throws TemplateParserException {
		if (ch >= '0' && ch <= '9') return ch - '0';
		else if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
		else if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
		else throw new TemplateParserException("incorrect character in rgb value: " + ch);
	}
	
}
